﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace Xceed.Silverlight.DataGrid
{
  public class LightTrailingControl : Control
  {
    #region Static Fields

    private const int FadeInDuration = 75;
    private const int StableDuration = 1000;
    private const int FadeOutDuration = 5000;
    private const int MouseLeaveFadeOutDuration = 250;

    private const int MouseDownFadeInAnimationDuration = 250;
    private const int MouseDownStableDuration = 250;
    private const int MouseDownFadeOutAnimationDuration = 250;

    private const int MouseUpFadeOutAnimationDuration = 250;

    #endregion

    #region Constructor

    public LightTrailingControl()
    {
      this.DefaultStyleKey = typeof( LightTrailingControl );

      this.Opacity = 0d;

      m_brush = new RadialGradientBrush();
      m_firstGradientStop = new GradientStop();
      m_secondGradientStop = new GradientStop();
      m_thirdGradientStop = new GradientStop();
      m_fourthGradientStop = new GradientStop();

      m_brush.GradientStops.Add( m_firstGradientStop );
      m_brush.GradientStops.Add( m_secondGradientStop );
      m_brush.GradientStops.Add( m_thirdGradientStop );
      m_brush.GradientStops.Add( m_fourthGradientStop );


      this.GradientOrigin = new Point( 0.5, 1.3 );
      this.GradientCenter = new Point( 0.5, 0.9 );
      this.GradientRadiusX = 0.5;
      this.GradientRadiusY = 0.5;

      this.FirstGradientColor = Color.FromArgb( 0xBF, 0x00, 0xEB, 0xFF );
      this.FirstGradientOffset = 0.004;

      this.SecondGradientColor = Color.FromArgb( 0x00, 0x00, 0x5A, 0xDA );
      this.SecondGradientOffset = 1d;

      this.ThirdGradientColor = this.SecondGradientColor;
      this.ThirdGradientOffset = this.SecondGradientOffset;

      this.FourthGradientColor = this.SecondGradientColor;
      this.FourthGradientOffset = this.SecondGradientOffset;

      this.FirstMouseDownGradientColor = Colors.Red;
      this.FirstMouseDownGradientOffset = 0.004;

      this.SecondMouseDownGradientColor = Colors.Orange;
      this.SecondMouseDownGradientOffset = 1d;

      this.ThirdMouseDownGradientColor = this.SecondMouseDownGradientColor;
      this.ThirdMouseDownGradientOffset = this.SecondMouseDownGradientOffset;

      this.FourthMouseDownGradientColor = this.SecondMouseDownGradientColor;
      this.FourthMouseDownGradientOffset = this.SecondMouseDownGradientOffset;


      m_firstGradientStop.Color = m_firstGradientColor;
      m_firstGradientStop.Offset = m_firstGradientOffset;
      m_secondGradientStop.Color = m_secondGradientColor;
      m_secondGradientStop.Offset = m_secondGradientOffset;
      m_thirdGradientStop.Color = m_secondGradientColor;
      m_thirdGradientStop.Offset = m_secondGradientOffset;
      m_fourthGradientStop.Color = m_secondGradientColor;
      m_fourthGradientStop.Offset = m_secondGradientOffset;

      this.Background = m_brush;
    }

    #endregion

    #region GradientOrigin Property

    public Point GradientOrigin
    {
      get
      {
        return m_brush.GradientOrigin;
      }
      set
      {
        m_brush.GradientOrigin = value;
      }
    }

    #endregion

    #region GradientCenter Property

    public Point GradientCenter
    {
      get
      {
        return m_brush.Center;
      }
      set
      {
        m_brush.Center = value;
      }
    }

    #endregion

    #region GradientRadiusX Property

    public double GradientRadiusX
    {
      get
      {
        return m_brush.RadiusX;
      }
      set
      {
        m_brush.RadiusX = value;
      }
    }

    #endregion

    #region GradientRadiusY Property

    public double GradientRadiusY
    {
      get
      {
        return m_brush.RadiusY;
      }
      set
      {
        m_brush.RadiusY = value;
      }
    }

    #endregion

    #region FirstGradientColor Property

    public Color FirstGradientColor
    {
      get
      {
        return m_firstGradientColor;
      }
      set
      {
        m_firstGradientColor = value;
      }
    }

    private Color m_firstGradientColor;

    #endregion

    #region SecondGradientColor Property

    public Color SecondGradientColor
    {
      get
      {
        return m_secondGradientColor;
      }
      set
      {
        m_secondGradientColor = value;
      }
    }

    private Color m_secondGradientColor;

    #endregion

    #region FirstGradientOffset Property

    public double FirstGradientOffset
    {
      get
      {
        return m_firstGradientOffset;
      }
      set
      {
        m_firstGradientOffset = value;
      }
    }

    private double m_firstGradientOffset;

    #endregion

    #region SecondGradientOffset Property

    public double SecondGradientOffset
    {
      get
      {
        return m_secondGradientOffset;
      }
      set
      {
        m_secondGradientOffset = value;
      }
    }

    private double m_secondGradientOffset;

    #endregion

    #region FirstMouseDownGradientColor Property

    public Color FirstMouseDownGradientColor
    {
      get
      {
        return m_firstMouseDownColor;
      }
      set
      {
        m_firstMouseDownColor = value;
      }
    }

    private Color m_firstMouseDownColor;

    #endregion

    #region SecondMouseDownGradientColor Property

    public Color SecondMouseDownGradientColor
    {
      get
      {
        return m_secondMouseDownColor;
      }
      set
      {
        m_secondMouseDownColor = value;
      }
    }

    private Color m_secondMouseDownColor;

    #endregion

    #region FirstMouseDownGradientOffset Property

    public double FirstMouseDownGradientOffset
    {
      get
      {
        return m_firstMouseDownOffset;
      }
      set
      {
        m_firstMouseDownOffset = value;
      }
    }

    private double m_firstMouseDownOffset;

    #endregion

    #region SecondMouseDownGradientOffset Property

    public double SecondMouseDownGradientOffset
    {
      get
      {
        return m_secondMouseDownOffset;
      }
      set
      {
        m_secondMouseDownOffset = value;
      }
    }

    private double m_secondMouseDownOffset;

    #endregion

    #region ThirdGradientColor Property

    public Color ThirdGradientColor
    {
      get
      {
        return m_thirdGradientColor;
      }
      set
      {
        m_thirdGradientColor = value;
      }
    }

    private Color m_thirdGradientColor;

    #endregion

    #region FourthGradientColor Property

    public Color FourthGradientColor
    {
      get
      {
        return m_fourthGradientColor;
      }
      set
      {
        m_fourthGradientColor = value;
      }
    }

    private Color m_fourthGradientColor;

    #endregion

    #region ThirdGradientOffset Property

    public double ThirdGradientOffset
    {
      get
      {
        return m_thirdGradientOffset;
      }
      set
      {
        m_thirdGradientOffset = value;
      }
    }

    private double m_thirdGradientOffset;

    #endregion

    #region FourthGradientOffset Property

    public double FourthGradientOffset
    {
      get
      {
        return m_fourthGradientOffset;
      }
      set
      {
        m_fourthGradientOffset = value;
      }
    }

    private double m_fourthGradientOffset;

    #endregion

    #region ThirdMouseDownGradientColor Property

    public Color ThirdMouseDownGradientColor
    {
      get
      {
        return m_thirdMouseDownColor;
      }
      set
      {
        m_thirdMouseDownColor = value;
      }
    }

    private Color m_thirdMouseDownColor;

    #endregion

    #region FourthMouseDownGradientColor Property

    public Color FourthMouseDownGradientColor
    {
      get
      {
        return m_fourthMouseDownColor;
      }
      set
      {
        m_fourthMouseDownColor = value;
      }
    }

    private Color m_fourthMouseDownColor;

    #endregion

    #region ThirdMouseDownGradientOffset Property

    public double ThirdMouseDownGradientOffset
    {
      get
      {
        return m_thirdMouseDownOffset;
      }
      set
      {
        m_thirdMouseDownOffset = value;
      }
    }

    private double m_thirdMouseDownOffset;

    #endregion

    #region FourthMouseDownGradientOffset Property

    public double FourthMouseDownGradientOffset
    {
      get
      {
        return m_fourthMouseDownOffset;
      }
      set
      {
        m_fourthMouseDownOffset = value;
      }
    }

    private double m_fourthMouseDownOffset;

    #endregion

    #region TrailingPosition Private Property

    private static readonly DependencyProperty TrailingPositionProperty = DependencyProperty.Register(
      "TrailingPosition",
      typeof( double ),
      typeof( LightTrailingControl ),
      new PropertyMetadata(
        ( double )0d,
        new PropertyChangedCallback( LightTrailingControl.OnTrailingPositionChanged ) ) );

    private double TrailingPosition
    {
      get
      {
        return ( double )this.GetValue( LightTrailingControl.TrailingPositionProperty );
      }
      set
      {
        this.SetValue( LightTrailingControl.TrailingPositionProperty, value );
      }
    }

    private static void OnTrailingPositionChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      LightTrailingControl control = ( LightTrailingControl )sender;

      if( control == null )
        return;

      control.m_brush.Center = new Point( ( double )e.NewValue, 1 );
      control.m_brush.GradientOrigin = new Point( ( double )e.NewValue, 1 );
    }

    #endregion

    #region GeneratedOpacityFadeInDuration Private Property

    private int GeneratedOpacityFadeInDuration
    {
      get
      {
        return m_generatedOpacityFadeInDuration;
      }
      set
      {
        m_generatedOpacityFadeInDuration = Math.Max( value, 0 );
      }
    }

    private int m_generatedOpacityFadeInDuration;

    #endregion

    #region GeneratedMouseDownFadeInDuration Private Property

    private int GeneratedMouseDownFadeInDuration
    {
      get
      {
        return m_generatedMouseDownFadeInDuration;
      }
      set
      {
        m_generatedMouseDownFadeInDuration = Math.Max( value, 0 );
      }
    }

    private int m_generatedMouseDownFadeInDuration;

    #endregion

    #region GeneratedOpacityFadeInStartValue Private Property

    private double GeneratedOpacityFadeInStartValue
    {
      get
      {
        return m_generatedOpacityFadeInStartValue;
      }
      set
      {
        double newValue = ( !double.IsNaN( value ) ) ? value : 0d;

        if( newValue < 0d )
        {
          newValue = 0d;
        }
        else if( newValue > 1d )
        {
          newValue = 1d;
        }

        m_generatedOpacityFadeInStartValue = newValue;
      }
    }

    private double m_generatedOpacityFadeInStartValue;

    #endregion

    #region FirstColorAnimationStartValue Private Property

    private Color FirstColorAnimationStartValue
    {
      get;
      set;
    }

    #endregion

    #region SecondColorAnimationStartValue Private Property

    private Color SecondColorAnimationStartValue
    {
      get;
      set;
    }

    #endregion

    #region ThirdColorAnimationStartValue Private Property

    private Color ThirdColorAnimationStartValue
    {
      get;
      set;
    }

    #endregion

    #region FourthColorAnimationStartValue Private Property

    private Color FourthColorAnimationStartValue
    {
      get;
      set;
    }

    #endregion

    #region FirstOffsetAnimationStartValue Private Property

    private double FirstOffsetAnimationStartValue
    {
      get;
      set;
    }

    #endregion

    #region SecondOffsetAnimationStartValue Private Property

    private double SecondOffsetAnimationStartValue
    {
      get;
      set;
    }

    #endregion

    #region ThirdOffsetAnimationStartValue Private Property

    private double ThirdOffsetAnimationStartValue
    {
      get;
      set;
    }

    #endregion

    #region FourthOffsetAnimationStartValue Private Property

    private double FourthOffsetAnimationStartValue
    {
      get;
      set;
    }

    #endregion

    #region OpacityAnimationStartTime Private Property

    private int OpacityAnimationStartTime
    {
      get;
      set;
    }

    #endregion

    #region MouseDownAnimationStartTime Private Property

    private int MouseDownAnimationStartTime
    {
      get;
      set;
    }

    #endregion

    #region IsAnimationRunning Private Property

    private bool IsAnimationRunning
    {
      get;
      set;
    }

    #endregion

    #region IsOpacityAnimating Private Property

    private bool IsOpacityAnimating
    {
      get;
      set;
    }

    #endregion

    #region IsMouseDownAnimating Private Property

    private bool IsMouseDownAnimating
    {
      get;
      set;
    }

    #endregion

    #region HasMouseLeft Private Property

    private bool HasMouseLeft
    {
      get;
      set;
    }

    #endregion

    #region IsMouseUp Private Property

    private bool IsMouseUp
    {
      get;
      set;
    }

    #endregion

    protected override void OnMouseEnter( MouseEventArgs e )
    {
      base.OnMouseEnter( e );

      m_firstGradientStop.Color = this.FirstGradientColor;
      m_firstGradientStop.Offset = this.FirstGradientOffset;
      m_secondGradientStop.Color = this.SecondGradientColor;
      m_secondGradientStop.Offset = this.SecondGradientOffset;
      m_thirdGradientStop.Color = this.ThirdGradientColor;
      m_thirdGradientStop.Offset = this.ThirdGradientOffset;
      m_fourthGradientStop.Color = this.FourthGradientColor;
      m_fourthGradientStop.Offset = this.FourthGradientOffset;
    }

    protected override void OnMouseMove( MouseEventArgs e )
    {
      //Compute position and set on the Trailing Position
      double controlWidth = this.ActualWidth;
      Point mousePoint = e.GetPosition( this );
      double centerOrigin = mousePoint.X / controlWidth;

      this.TrailingPosition = centerOrigin;

      //Launch the animation for opacity
      this.StartOpacityAnimation();

      base.OnMouseMove( e );
    }

    protected override void OnMouseLeave( MouseEventArgs e )
    {

      if( this.IsAnimationRunning )
      {
        this.StartMouseLeaveAnimation();
      }

      base.OnMouseLeave( e );

    }

    protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )
    {
      base.OnMouseLeftButtonDown( e );

      this.StartOpacityAnimation();
      this.StartMouseDownAnimation();
    }

    protected override void OnMouseLeftButtonUp( MouseButtonEventArgs e )
    {
      base.OnMouseLeftButtonUp( e );

      this.StartOpacityAnimation();
      this.StartMouseUpAnimation();

    }

    private void StartOpacityAnimation()
    {
      this.OpacityAnimationStartTime = Environment.TickCount;
      this.HasMouseLeft = false;

      if( !this.IsAnimationRunning )
      {
        CompositionTarget.Rendering += OnRendering;
        this.IsAnimationRunning = true;
      }

      double opacity = this.Opacity;

      this.GeneratedOpacityFadeInDuration = ( int )( ( 1d - opacity ) * LightTrailingControl.FadeInDuration );
      this.GeneratedOpacityFadeInStartValue = opacity;

      this.IsOpacityAnimating = true;
    }

    private void StartMouseLeaveAnimation()
    {
      this.OpacityAnimationStartTime = Environment.TickCount;
      this.HasMouseLeft = true;

      double opacity = this.Opacity;

      this.GeneratedOpacityFadeInDuration = ( int )( LightTrailingControl.MouseLeaveFadeOutDuration - ( ( 1d - opacity ) * LightTrailingControl.MouseLeaveFadeOutDuration ) );
      this.GeneratedOpacityFadeInStartValue = opacity;

      this.IsOpacityAnimating = true;
    }

    private void StartMouseDownAnimation()
    {
      this.MouseDownAnimationStartTime = Environment.TickCount;

      if( !this.IsAnimationRunning )
      {
        CompositionTarget.Rendering += OnRendering;
        this.IsAnimationRunning = true;
      }

      double totalOffsetTravel = m_firstMouseDownOffset - m_firstGradientOffset;

      if( Math.Abs( totalOffsetTravel ) < double.Epsilon )
      {
        double offsetProgress = ( m_firstGradientStop.Offset - m_firstGradientOffset ) / totalOffsetTravel;

        this.GeneratedMouseDownFadeInDuration = ( int )( ( 1d - offsetProgress ) * LightTrailingControl.MouseDownFadeInAnimationDuration );
      }
      else
      {
        this.GeneratedMouseDownFadeInDuration = 0;
      }

      this.FirstColorAnimationStartValue = m_firstGradientStop.Color;
      this.FirstOffsetAnimationStartValue = m_firstGradientStop.Offset;

      this.SecondColorAnimationStartValue = m_secondGradientStop.Color;
      this.SecondOffsetAnimationStartValue = m_secondGradientStop.Offset;

      this.ThirdColorAnimationStartValue = m_thirdGradientStop.Color;
      this.ThirdOffsetAnimationStartValue = m_thirdGradientStop.Offset;

      this.FourthColorAnimationStartValue = m_fourthGradientStop.Color;
      this.FourthOffsetAnimationStartValue = m_fourthGradientStop.Offset;

      this.IsMouseUp = false;

      this.IsMouseDownAnimating = true;
    }

    private void StartMouseUpAnimation()
    {
      this.MouseDownAnimationStartTime = Environment.TickCount;

      if( !this.IsAnimationRunning )
      {
        CompositionTarget.Rendering += OnRendering;
        this.IsAnimationRunning = true;
      }

      double totalOffsetTravel = m_firstGradientOffset - m_firstMouseDownOffset;

      if( Math.Abs( totalOffsetTravel ) < double.Epsilon )
      {
        double offsetProgress = ( m_firstGradientStop.Offset - m_firstMouseDownOffset ) / totalOffsetTravel;

        this.GeneratedMouseDownFadeInDuration = ( int )( ( 1d - offsetProgress ) * LightTrailingControl.MouseUpFadeOutAnimationDuration );
      }
      else
      {
        this.GeneratedMouseDownFadeInDuration = 0;
      }

      this.FirstColorAnimationStartValue = m_firstGradientStop.Color;
      this.FirstOffsetAnimationStartValue = m_firstGradientStop.Offset;

      this.SecondColorAnimationStartValue = m_secondGradientStop.Color;
      this.SecondOffsetAnimationStartValue = m_secondGradientStop.Offset;

      this.ThirdColorAnimationStartValue = m_thirdGradientStop.Color;
      this.ThirdOffsetAnimationStartValue = m_thirdGradientStop.Offset;

      this.FourthColorAnimationStartValue = m_fourthGradientStop.Color;
      this.FourthOffsetAnimationStartValue = m_fourthGradientStop.Offset;

      this.IsMouseUp = true;

      this.IsMouseDownAnimating = true;
    }

    private void OnRendering( object sender, EventArgs e )
    {
      if( this.IsOpacityAnimating )
      {
        int opacityElapsedTime = Environment.TickCount - this.OpacityAnimationStartTime;

        double opacity;

        if( !this.HasMouseLeft )
        {
          if( opacityElapsedTime <= this.GeneratedOpacityFadeInDuration )
          {
            double startValue = this.GeneratedOpacityFadeInStartValue;

            if( this.GeneratedOpacityFadeInDuration > 0 )
            {
              opacity = startValue + ( ( 1d - startValue ) * ( 1d * opacityElapsedTime / this.GeneratedOpacityFadeInDuration ) );
            }
            else
            {
              opacity = startValue;
            }
          }
          else
          {
            opacityElapsedTime -= this.GeneratedOpacityFadeInDuration;

            if( opacityElapsedTime <= LightTrailingControl.StableDuration )
            {
              opacity = 1d;
            }
            else
            {
              opacityElapsedTime -= LightTrailingControl.StableDuration;

              if( opacityElapsedTime <= LightTrailingControl.FadeOutDuration )
              {
                opacity = 1d - ( 1d * opacityElapsedTime / LightTrailingControl.FadeOutDuration );
              }
              else
              {
                opacity = 0d;
                this.IsOpacityAnimating = false;
              }
            }
          }
        }
        else
        {
          if( opacityElapsedTime < this.GeneratedOpacityFadeInDuration )
          {
            double startValue = this.GeneratedOpacityFadeInStartValue;

            if( this.GeneratedOpacityFadeInDuration > 0 )
            {
              opacity = startValue - ( startValue * ( 1d * opacityElapsedTime / this.GeneratedOpacityFadeInDuration ) );
            }
            else
            {
              opacity = 0d;
            }
          }
          else
          {
            opacity = 0d;
            this.IsOpacityAnimating = false;
          }
        }

        this.Opacity = opacity;
      }

      if( this.IsMouseDownAnimating )
      {
        int mouseDownElapsedTime = Environment.TickCount - this.MouseDownAnimationStartTime;

        if( !this.IsMouseUp )
        {
          int generatedMouseDownFadeInDuration = this.GeneratedMouseDownFadeInDuration;

          if( mouseDownElapsedTime < generatedMouseDownFadeInDuration )
          {
            double elapsedTime = ( mouseDownElapsedTime * 1d ) / generatedMouseDownFadeInDuration;

            m_firstGradientStop.Color = this.ComputeColor( this.FirstColorAnimationStartValue, m_firstMouseDownColor, elapsedTime );
            m_firstGradientStop.Offset = this.ComputeOffset( this.FirstOffsetAnimationStartValue, m_firstMouseDownOffset, elapsedTime );

            m_secondGradientStop.Color = this.ComputeColor( this.SecondColorAnimationStartValue, m_secondMouseDownColor, elapsedTime );
            m_secondGradientStop.Offset = this.ComputeOffset( this.SecondOffsetAnimationStartValue, m_secondMouseDownOffset, elapsedTime );

            m_thirdGradientStop.Color = this.ComputeColor( this.ThirdColorAnimationStartValue, m_thirdMouseDownColor, elapsedTime );
            m_thirdGradientStop.Offset = this.ComputeOffset( this.ThirdOffsetAnimationStartValue, m_thirdMouseDownOffset, elapsedTime );

            m_fourthGradientStop.Color = this.ComputeColor( this.FourthColorAnimationStartValue, m_fourthMouseDownColor, elapsedTime );
            m_fourthGradientStop.Offset = this.ComputeOffset( this.FourthOffsetAnimationStartValue, m_fourthMouseDownOffset, elapsedTime );
          }
          else
          {
            mouseDownElapsedTime -= generatedMouseDownFadeInDuration;

            if( mouseDownElapsedTime < LightTrailingControl.MouseDownStableDuration )
            {
              m_firstGradientStop.Color = m_firstMouseDownColor;
              m_firstGradientStop.Offset = m_firstMouseDownOffset;

              m_secondGradientStop.Color = m_secondMouseDownColor;
              m_secondGradientStop.Offset = m_secondMouseDownOffset;

              m_thirdGradientStop.Color = m_thirdMouseDownColor;
              m_thirdGradientStop.Offset = m_thirdMouseDownOffset;

              m_fourthGradientStop.Color = m_fourthMouseDownColor;
              m_fourthGradientStop.Offset = m_fourthMouseDownOffset;
            }
            else
            {
              mouseDownElapsedTime -= LightTrailingControl.MouseDownStableDuration;

              if( mouseDownElapsedTime < LightTrailingControl.MouseDownFadeOutAnimationDuration )
              {
                double elapsedTime = ( mouseDownElapsedTime * 1d ) / LightTrailingControl.MouseDownFadeOutAnimationDuration;

                m_firstGradientStop.Color = this.ComputeColor( m_firstMouseDownColor, m_firstGradientColor, elapsedTime );
                m_firstGradientStop.Offset = this.ComputeOffset( m_firstMouseDownOffset, m_firstGradientOffset, elapsedTime );

                m_secondGradientStop.Color = this.ComputeColor( m_secondMouseDownColor, m_secondGradientColor, elapsedTime );
                m_secondGradientStop.Offset = this.ComputeOffset( m_secondMouseDownOffset, m_secondGradientOffset, elapsedTime );

                m_thirdGradientStop.Color = this.ComputeColor( m_thirdMouseDownColor, m_thirdGradientColor, elapsedTime );
                m_thirdGradientStop.Offset = this.ComputeOffset( m_thirdMouseDownOffset, m_thirdGradientOffset, elapsedTime );

                m_fourthGradientStop.Color = this.ComputeColor( m_fourthMouseDownColor, m_fourthGradientColor, elapsedTime );
                m_fourthGradientStop.Offset = this.ComputeOffset( m_fourthMouseDownOffset, m_fourthGradientOffset, elapsedTime );
              }
              else
              {
                m_firstGradientStop.Color = m_firstGradientColor;
                m_firstGradientStop.Offset = m_firstGradientOffset;

                m_secondGradientStop.Color = m_secondGradientColor;
                m_secondGradientStop.Offset = m_secondGradientOffset;

                m_thirdGradientStop.Color = m_thirdGradientColor;
                m_thirdGradientStop.Offset = m_thirdGradientOffset;

                m_fourthGradientStop.Color = m_fourthGradientColor;
                m_fourthGradientStop.Offset = m_fourthGradientOffset;

                this.IsMouseDownAnimating = false;
              }
            }
          }
        }
        else
        {
          int generatedMouseDownFadeInDuration = this.GeneratedMouseDownFadeInDuration;

          if( mouseDownElapsedTime < generatedMouseDownFadeInDuration )
          {
            double elapsedTime = ( mouseDownElapsedTime * 1d ) / generatedMouseDownFadeInDuration;

            m_firstGradientStop.Color = this.ComputeColor( this.FirstColorAnimationStartValue, m_firstMouseDownColor, elapsedTime );
            m_firstGradientStop.Offset = this.ComputeOffset( this.FirstOffsetAnimationStartValue, m_firstMouseDownOffset, elapsedTime );

            m_secondGradientStop.Color = this.ComputeColor( this.SecondColorAnimationStartValue, m_secondMouseDownColor, elapsedTime );
            m_secondGradientStop.Offset = this.ComputeOffset( this.SecondOffsetAnimationStartValue, m_secondMouseDownOffset, elapsedTime );

            m_thirdGradientStop.Color = this.ComputeColor( this.ThirdColorAnimationStartValue, m_thirdMouseDownColor, elapsedTime );
            m_thirdGradientStop.Offset = this.ComputeOffset( this.ThirdOffsetAnimationStartValue, m_thirdMouseDownOffset, elapsedTime );

            m_fourthGradientStop.Color = this.ComputeColor( this.FourthColorAnimationStartValue, m_fourthMouseDownColor, elapsedTime );
            m_fourthGradientStop.Offset = this.ComputeOffset( this.FourthOffsetAnimationStartValue, m_fourthMouseDownOffset, elapsedTime );
          }
          else
          {
            m_firstGradientStop.Color = m_firstGradientColor;
            m_firstGradientStop.Offset = m_firstGradientOffset;

            m_secondGradientStop.Color = m_secondGradientColor;
            m_secondGradientStop.Offset = m_secondGradientOffset;

            m_thirdGradientStop.Color = m_thirdGradientColor;
            m_thirdGradientStop.Offset = m_thirdGradientOffset;

            m_fourthGradientStop.Color = m_fourthGradientColor;
            m_fourthGradientStop.Offset = m_fourthGradientOffset;

            this.IsMouseDownAnimating = false;
          }
        }
      }

      if( !this.IsOpacityAnimating && !this.IsMouseDownAnimating )
      {
        CompositionTarget.Rendering -= this.OnRendering;
        this.IsAnimationRunning = false;
      }
    }

    private double ComputeOffset( double startOffset, double targetOffset, double elapsedTime )
    {
      if( startOffset.Equals( targetOffset ) )
        return startOffset;

      if( elapsedTime <= 0d )
        return startOffset;

      if( elapsedTime >= 1d )
        return targetOffset;

      return startOffset + ( ( targetOffset - startOffset ) * elapsedTime );
    }

    private Color ComputeColor( Color startColor, Color targetColor, double elapsedTime )
    {
      if( startColor.Equals( targetColor ) )
        return startColor;

      if( elapsedTime <= 0d )
        return startColor;

      if( elapsedTime >= 1d )
        return targetColor;

      byte newA = ( byte )( startColor.A + ( ( targetColor.A - startColor.A ) * elapsedTime ) );
      byte newR = ( byte )( startColor.R + ( ( targetColor.R - startColor.R ) * elapsedTime ) );
      byte newG = ( byte )( startColor.G + ( ( targetColor.G - startColor.G ) * elapsedTime ) );
      byte newB = ( byte )( startColor.B + ( ( targetColor.B - startColor.B ) * elapsedTime ) );

      return Color.FromArgb( newA, newR, newG, newB );
    }

    #region Private Fields

    private RadialGradientBrush m_brush;
    private GradientStop m_firstGradientStop;
    private GradientStop m_secondGradientStop;
    private GradientStop m_thirdGradientStop;
    private GradientStop m_fourthGradientStop;

    #endregion
  }
}
